# rtasr_client.py - 修复版语音识别客户端
import hashlib
import hmac
import base64
import json
import time
import threading
import logging
from urllib.parse import quote

logger = logging.getLogger(__name__)

# 检查依赖
try:
    import websocket

    WEBSOCKET_AVAILABLE = True
    logger.info("WebSocket模块可用")
except ImportError:
    WEBSOCKET_AVAILABLE = False
    logger.warning("WebSocket模块不可用，安装：pip install websocket-client")

try:
    import pyaudio

    PYAUDIO_AVAILABLE = True
    logger.info("PyAudio模块可用")
except ImportError:
    PYAUDIO_AVAILABLE = False
    logger.warning("PyAudio模块不可用，安装：pip install pyaudio")


class RtAsrClient:
    """实时语音识别客户端"""

    def __init__(self, app_id, api_key):
        self.app_id = app_id
        self.api_key = api_key

        # 音频配置
        self.FORMAT = pyaudio.paInt16 if PYAUDIO_AVAILABLE else None
        self.CHANNELS = 1
        self.RATE = 16000
        self.CHUNK = 640

        # 连接状态
        self.ws = None
        self.connected = False
        self.running = False
        self.recognition_thread = None
        self.connection_error = ""

        # 识别结果
        self.result_text = ""
        self.last_text = ""

        logger.info(f"RtAsrClient初始化: app_id={app_id[:8]}...")

    def connect(self):
        """连接到讯飞实时语音识别服务"""
        if not WEBSOCKET_AVAILABLE:
            logger.error("WebSocket不可用，无法连接语音识别服务")
            self.connection_error = "WebSocket模块未安装"
            return False

        # 检查API凭据
        if not self.app_id or self.app_id == "your_rtasr_app_id":
            logger.error("RTASR_APPID未正确配置")
            self.connection_error = "RTASR_APPID未配置"
            return False

        if not self.api_key or self.api_key == "your_rtasr_api_key":
            logger.error("RTASR_APIKey未正确配置")
            self.connection_error = "RTASR_APIKey未配置"
            return False

        max_retries = 3
        for attempt in range(max_retries):
            try:
                # 如果已有连接，先关闭
                if self.ws:
                    try:
                        self.ws.close()
                    except:
                        pass
                    self.ws = None

                # 生成认证参数
                ts = str(int(time.time()))
                base_string = hashlib.md5((self.app_id + ts).encode()).hexdigest()
                signa = hmac.new(self.api_key.encode(), base_string.encode(), hashlib.sha1).digest()
                signa = base64.b64encode(signa).decode()

                # 构建WebSocket URL
                url = f"wss://rtasr.xfyun.cn/v1/ws?appid={self.app_id}&ts={ts}&signa={quote(signa)}"
                logger.info(f"连接语音识别服务 (尝试 {attempt + 1}/{max_retries})")

                # 创建WebSocket连接
                self.ws = websocket.create_connection(url, timeout=10)

                self.running = True
                self.connected = True

                # 启动接收线程
                if self.recognition_thread and self.recognition_thread.is_alive():
                    self.running = False
                    self.recognition_thread.join(timeout=1)

                self.recognition_thread = threading.Thread(target=self._recv_loop, daemon=True)
                self.recognition_thread.start()

                logger.info("✅ 语音识别服务连接成功")
                return True

            except Exception as e:
                logger.error(f"❌ 连接尝试 {attempt + 1} 失败: {str(e)}")
                self.connection_error = str(e)
                self.connected = False

                if attempt < max_retries - 1:
                    wait_time = 2 ** attempt
                    logger.info(f"等待 {wait_time} 秒后重试...")
                    time.sleep(wait_time)

        logger.error(f"❌ 连接失败，已尝试 {max_retries} 次")
        return False

    def _recv_loop(self):
        """接收识别结果的循环"""
        try:
            while self.running and self.connected and self.ws:
                try:
                    res = self.ws.recv()

                    if not res:
                        continue

                    data = json.loads(res)

                    if data.get("action") == "result":
                        self._handle_result(data["data"])
                    elif data.get("action") == "error":
                        logger.error(f"识别服务错误: {data}")
                        self.connection_error = data.get("desc", "未知错误")
                        break

                except Exception as e:
                    error_msg = str(e).lower()
                    if "connection" in error_msg and "closed" in error_msg:
                        logger.error(f"连接已关闭: {str(e)}")
                        break
                    elif "timeout" not in error_msg:
                        logger.warning(f"接收消息异常: {str(e)}")
                        time.sleep(0.1)

        except Exception as e:
            logger.error(f"接收循环严重异常: {str(e)}")
        finally:
            if self.running:
                logger.info("接收循环正常结束")
            else:
                logger.info("接收循环被停止")
                self.connected = False

    def _handle_result(self, data_str):
        """处理识别结果"""
        try:
            data = json.loads(data_str)

            # 解析识别结果
            for rt in data.get("cn", {}).get("st", {}).get("rt", []):
                for ws in rt.get("ws", []):
                    for cw in ws.get("cw", []):
                        word = cw.get("w", "")
                        if word and word != self.last_text:
                            self.result_text += word
                            self.last_text = word
                            logger.debug(f"识别到文字: {word}")

        except Exception as e:
            logger.error(f"处理识别结果失败: {str(e)}")

    def record_and_send(self, duration_sec=10):
        """录音并发送音频数据进行识别"""
        # 检查连接状态
        if not self.is_connected():
            logger.error("语音识别服务未连接")
            return self._simulate_recording(duration_sec)

        # PyAudio检查
        if not PYAUDIO_AVAILABLE:
            logger.error("❌ PyAudio不可用，无法录制真实音频")
            logger.error("💡 解决方案：")
            logger.error("   Windows: pip install pipwin && pipwin install pyaudio")
            logger.error("   macOS: brew install portaudio && pip install pyaudio")
            logger.error("   Ubuntu: sudo apt-get install portaudio19-dev && pip install pyaudio")
            return self._simulate_recording(duration_sec)

        try:
            logger.info(f"🎙️ 开始真实语音录制，时长: {duration_sec}秒")
            logger.info("🔊 请现在开始说话...")

            # 初始化PyAudio
            audio = pyaudio.PyAudio()

            # 检查输入设备
            device_count = audio.get_device_count()
            input_devices = []
            for i in range(device_count):
                device_info = audio.get_device_info_by_index(i)
                if device_info['maxInputChannels'] > 0:
                    input_devices.append(device_info)

            if not input_devices:
                logger.error("❌ 未找到可用的输入设备")
                audio.terminate()
                return self._simulate_recording(duration_sec)

            logger.info(f"📱 找到 {len(input_devices)} 个输入设备")

            # 打开音频流
            try:
                stream = audio.open(
                    format=self.FORMAT,
                    channels=self.CHANNELS,
                    rate=self.RATE,
                    input=True,
                    frames_per_buffer=self.CHUNK,
                    input_device_index=None  # 使用默认设备
                )
            except Exception as e:
                logger.error(f"❌ 打开音频流失败: {e}")
                audio.terminate()
                return self._simulate_recording(duration_sec)

            # 计算总帧数
            total_frames = int(self.RATE / self.CHUNK * duration_sec)

            logger.info("🎤 开始录音和实时发送到讯飞服务器...")

            success_frames = 0
            for i in range(total_frames):
                if not self.running or not self.is_connected():
                    logger.warning("录音中断：连接断开或被停止")
                    break

                try:
                    # 读取音频数据
                    data = stream.read(self.CHUNK, exception_on_overflow=False)

                    # 发送到讯飞服务器
                    if self.ws and self.connected:
                        self.ws.send(data)
                        success_frames += 1

                        # 每秒记录一次进度
                        if i % (self.RATE // self.CHUNK) == 0:
                            seconds_passed = i // (self.RATE // self.CHUNK)
                            logger.info(f"录音进度: {seconds_passed}/{duration_sec}秒")
                    else:
                        logger.warning("连接断开，停止发送音频数据")
                        break

                    # 控制发送速率
                    time.sleep(0.02)

                except Exception as e:
                    logger.error(f"录音数据处理失败: {str(e)}")
                    break

            # 停止录音
            stream.stop_stream()
            stream.close()
            audio.terminate()

            # 发送结束标志
            if self.ws and self.connected:
                try:
                    self.ws.send(bytes('{"end": true}', encoding="utf-8"))
                    logger.info("✅ 发送录音结束标志给讯飞服务器")
                except Exception as e:
                    logger.warning(f"发送结束标志失败: {e}")

            logger.info(f"🎤 真实语音录制完成，成功发送 {success_frames}/{total_frames} 帧")

            # 等待识别结果
            time.sleep(1)

            return True

        except Exception as e:
            logger.error(f"❌ 真实语音录制失败: {str(e)}")
            logger.error("⚠️ 降级到模拟录音模式")
            return self._simulate_recording(duration_sec)

    def _simulate_recording(self, duration_sec):
        """模拟录音模式"""
        logger.warning(f"⚠️ 使用模拟录音模式，时长: {duration_sec}秒")
        logger.warning("⚠️ 模拟原因：PyAudio不可用、设备问题或连接失败")

        # 模拟录音过程
        for i in range(duration_sec):
            if not self.running:
                break
            time.sleep(1)

            if i % 10 == 0:
                logger.info(f"模拟录音进度: {i}/{duration_sec}秒")

        logger.warning("✅ 模拟录音完成 - 注意：未获取到真实语音")
        logger.warning("💡 建议：")
        logger.warning("   1. 检查麦克风权限设置")
        logger.warning("   2. 确保PyAudio正确安装")
        logger.warning("   3. 检查音频设备连接")
        logger.warning("   4. 验证API密钥配置")

        return True

    def get_result(self):
        """获取识别结果"""
        # 等待处理完成
        time.sleep(0.5)
        result = self.result_text.strip()

        # 详细日志输出用于验证
        logger.info("🎤 语音识别结果获取:")
        logger.info(f"   原始结果长度: {len(self.result_text)}")
        logger.info(f"   处理后长度: {len(result)}")
        logger.info(f"   连接状态: {self.connected}")
        logger.info(f"   结果内容: '{result}'")

        if not result and self.connected:
            logger.warning("⚠️ 语音识别结果为空，可能原因:")
            logger.warning("   1. 用户未说话")
            logger.warning("   2. 环境噪音太大")
            logger.warning("   3. 麦克风权限问题")
            logger.warning("   4. 网络连接不稳定")
            logger.warning("   5. API配额不足")

        return result

    def clear_result(self):
        """清空识别结果"""
        self.result_text = ""
        self.last_text = ""
        logger.info("清空识别结果")

    def stop(self):
        """停止识别服务"""
        logger.info("停止语音识别服务")

        self.running = False
        self.connected = False

        if self.ws:
            try:
                self.ws.close()
            except:
                pass
            self.ws = None

        if self.recognition_thread and self.recognition_thread.is_alive():
            self.recognition_thread.join(timeout=2)

        logger.info("✅ 语音识别服务已停止")

    def is_connected(self):
        """检查连接状态"""
        if not self.connected or not self.ws:
            return False

        try:
            if hasattr(self.ws, 'sock') and self.ws.sock:
                return self.connected and self.running
            else:
                self.connected = False
                return False
        except:
            self.connected = False
            return False

    def get_error(self):
        """获取错误信息"""
        return self.connection_error

    def test_connection(self):
        """测试连接功能"""
        logger.info("🧪 测试语音识别连接...")

        if self.connect():
            logger.info("✅ 连接测试成功")
            self.stop()
            return True
        else:
            logger.error(f"❌ 连接测试失败: {self.get_error()}")
            return False


# 测试函数
def test_rtasr_client():
    """测试语音识别客户端"""
    print("🧪 测试语音识别客户端...")

    # 需要真实的API凭据进行测试
    app_id = "test_app_id"
    api_key = "test_api_key"

    try:
        client = RtAsrClient(app_id, api_key)

        print(f"WebSocket可用: {'✅' if WEBSOCKET_AVAILABLE else '❌'}")
        print(f"PyAudio可用: {'✅' if PYAUDIO_AVAILABLE else '❌'}")

        if WEBSOCKET_AVAILABLE:
            print("连接测试: 需要真实API凭据")
        else:
            print("❌ WebSocket不可用，跳过连接测试")

        return True

    except Exception as e:
        print(f"❌ 测试失败: {str(e)}")
        return False


if __name__ == "__main__":
    print("🔧 语音识别客户端模块测试")
    print("=" * 40)

    print(f"WebSocket可用: {'✅' if WEBSOCKET_AVAILABLE else '❌'}")
    print(f"PyAudio可用: {'✅' if PYAUDIO_AVAILABLE else '❌'}")

    test_result = test_rtasr_client()
    print(f"总体测试: {'✅' if test_result else '❌'}")

    print("=" * 40)